package simulatepushpull;

import java.util.Arrays;
import java.lang.Object.*;
import java.lang.Math;
import java.util.Random;

/**
 *
 * @author Owen
 */
public class SimulateKanban {
    private double inRate,nextIn,t, CT,throughput;
    private double[] times,nextOut, arrayIn, arrayOut;
    private int[] kanbansUsed = new int[3];
    private int[] results = new int[2];
    private int totalIn, orders, aantalKanbans, totalOuts = 0; 
    private Queue[] system = new Queue[4];
    private Random RNG;
    private Gamma Gamma; //a
    
  
    
    
    public SimulateKanban(Random RNG,double inR, int aantalKanbans, int orders) {
        inRate = inR;
        this.orders = orders; 
        arrayIn = new double[orders];
        arrayOut = new double[orders];
        this.RNG = RNG;
        Gamma = new Gamma(RNG);
        CT = 0;
        this.aantalKanbans = aantalKanbans;        
        System.out.println("Hamp");
        t = 0;
        nextOut= new double[3];
        nextIn = getNextIn();
        System.out.println(nextIn + " " + aantalKanbans);
        
        for (int i = 0; i < nextOut.length; i++) {
            nextOut[i] = Long.MAX_VALUE;
        }
        for (int i = 0; i < system.length; i++) {
            system[i] = new Queue();
        }
        for (int i = 0; i < kanbansUsed.length; i++) {
            kanbansUsed[i] = 0;
        }
        times = new double[]{nextIn, nextOut[0], nextOut[1], nextOut[2]};
        Arrays.sort(times);
    }
    
    
     public double getNextIn() {
        
        nextIn = t + Math.log(1-RNG.nextDouble())*(-12); 
        totalIn++;
        System.out.println("totalIn = " + totalIn);
        return nextIn;
    }    
    public double getNextOut(int id) {
        if (id == 0) {
            nextOut[id] = t + RNG.nextGaussian()*1.8+9;
        }
        if (id == 1) {
            nextOut[id] = t + Math.log(1-RNG.nextDouble())/-0.1;
        }
        if (id == 2) {
            nextOut[id] = t + Gamma.sampleGamma(4,2.4);
        }
        
        return nextOut[id];
    }     
    public int getServed(){
        System.out.println(system[2].getServed());
        return system[2].getServed();
    }
    public double getCT()
    {
        for (int i = 0 ; i < arrayIn.length; i++)
        {
            CT += (arrayOut[i] - arrayIn[i]);
            
        }
        CT = CT / arrayIn.length;
        return CT;
        
        
    }
    
    public double getTroughput()
    {
        throughput = arrayIn.length/(arrayOut[arrayIn.length -1] - arrayIn[0]);
        return throughput;
        
    }
    public void processNextEvent() {
        times[0] = nextIn;
        
        System.out.println("nextIn: " + nextIn);
        for (int i = 1; i < times.length; i++) 
        {
            times[i] = nextOut[i-1];
            int count = i - 1;
            System.out.println("nextOut[" + count + "]: " + nextOut[i-1]);
        }
        if (system[3].queued > 0)
            System.out.println("WACHTRIJ: " + system[3].queued);
        Arrays.sort(times);
        if(times[0] == nextIn && totalIn > orders)
        {
            for(int i = 0; i < times.length - 1; i++)
                  times[i] = times[i+1];
        }
        
        
        if (nextIn == times[0]) {
            System.out.println("NEXTIN WINS");
            t = nextIn;
            arrayIn[totalIn - 1] = nextIn;
            if (kanbansUsed[0] < aantalKanbans) //Mag product naar system0 gaan?
            {
            system[0].in();
            kanbansUsed[0] ++;
            getNextIn();
            if (system[0].getQueued() == 1) //Was queue0 nog leeg? Ja, steek doorgegeven product dan direct in machine0
                                                                               //STAPKE COMPLICATED 2
            {
                    getNextOut(0);
                    kanbansUsed[0]--; //Zien machine en kanban als 1 geheel, en brengen zo de machine in rekening
               
            }
            }
            else { //Product mag niet naar system0 gaan en steken we dus in queue3
                system[3].in();
                getNextIn();                
            }
            
        }
        
        if (nextOut[0] == times[0]) {
            System.out.println("NEXTOUT0 WINS");
            t = nextOut[0];
            system[0].out();
            system[1].in(); // Het product mag direct naar system1 gaan want nextOut0 wordt enkel gemaakt als system1 nog niet vol was
            kanbansUsed[1]++;
            if (system[0].getQueued() == 0) //Is queue0 nu leeg? Ja, steek dan niets in machine0, want er is niets.
                nextOut[0] = Long.MAX_VALUE; //NextOut0 wordt automatisch gegenereerd bij STAPKE COMPLICATED 2 van NextIn
            else                            //Is queue0 niet leeg? Er staan dus producten klaar
            {
                if(kanbansUsed[1] < aantalKanbans) //steek deze dan in machine1 als dit toegelaten is door kanbans1
                {
                    getNextOut(0);
                    kanbansUsed[0]--;

                }
                else //Als dit niet toegelaten is door kanbans1, steek dan niets in machine0
                {
                    nextOut[0] = Long.MAX_VALUE; //NextOut0 wordt automatisch gegenereerd als nextOut1 eerst is (bij STAPKE COMPLICATED)
                }                                   
                                                    //STAPKE COMPLICATED
                if(system[3].queued != 0)           //Je hebt nu een product in de machine gestoken en er is dus een kanban vrij,
                                                    //het vorige system mag dus een getNextout genereren als dit nog niet zou bestaan
                                                    //in dit specifieke getal is het vorige systeem gewoon tNextIn, en deze bestaat altijd, dus checken we gewoon queue3
                {
                    system[3].out();
                    system[0].in();
                    kanbansUsed[0]++;
                }                

            }
                
            if (system[1].getQueued() == 1) //STAPKE COMPLICATED 2, 
               getNextOut(1);                                                 //Was system1 nog leeg, steek doorgegeven product dan direct in machine1
                
        }
        if (nextOut[1] == times[0]) {
            System.out.println("NEXTOUT1 WINS");
            t = nextOut[1];
            system[1].out();
            system[2].in(); // Het product mag sowieso naar system2 gaan want nextOut1 wordt enkel gemaakt als system2 nog niet vol was
            kanbansUsed[2]++;
            if (system[1].getQueued() == 0) //Is queue1 nu leeg? Ja, steek dan niets in machine1, want er is niets.
                nextOut[1] = Long.MAX_VALUE; //NextOut1 wordt automatisch terug gegenereerd bij STAPKE COMPLICATED 2 van NextOut0
            else                            //Is queue1 niet leeg? Er staan dus producten klaar
            {
                if(kanbansUsed[2] < aantalKanbans) //steek deze dan in machine1 als dit toegelaten is door kanbans2
                {
                    getNextOut(1);
                    kanbansUsed[1]--;

                }
                else //Als dit niet toegelaten is door kanbans2, steek dan niets in machine1
                {
                    nextOut[1] = Long.MAX_VALUE; //NextOut1 wordt automatisch gegenereerd als nextOut 2eerst is (door STAPKE COMPLICATED)
                }                                                                             
                                                                             //STAPKE COMPLICATED
                if(nextOut[0] == Long.MAX_VALUE && system[0].queued !=0)     //Je hebt nu een product in de machine gestoken en er is dus een kanban vrij,
                getNextOut(0);                                               //het vorige system mag dus een NextOut0 genereren (iets in machine0 steken) als dit nog niet zou bestaan 
            }
               if (system[2].getQueued() == 1)                                 //STAPKE COMPLICATED 2, nextOut2 kan enkel MAX_VALUE zijn als ze leeg is
               getNextOut(2);                                                    //is system2 leeg, steek doorgegeven product dan direct in machine2
                
        }
        
        if (nextOut[2] == times[0]) {
            System.out.println("NEXTOUT2 WINS");
            t = nextOut[2];
            totalOuts++;
            arrayOut[totalOuts-1] = nextOut[2];
            system[2].out();    //Product is afgewerkt, weg uit gehele systeem, served wordt bijgehouden in queue2
            
            if (system[2].getQueued() == 0) //Is queue2 nu leeg? Ja, steek dan niets in machine2, want er is niets.
                nextOut[2] = Long.MAX_VALUE; //NextOut2 wordt automatisch terug gegenereerd bij STAPKE COMPLICATED 2 van NextOut1
            else                            //Is queue1 niet leeg? Er staan dus producten klaar
                {
                    getNextOut(2);          //Steek deze producten dan sowieso in machine2 (moet geen rekening meer houden met kanbans)
                    kanbansUsed[2]--;

                }
                                                                      
                                                                             //STAPKE COMPLICATED
                if(nextOut[1] == Long.MAX_VALUE && system[1].queued !=0)     //Je hebt nu een product in de machine gestoken en er is dus een kanban vrij,
                getNextOut(1);                                               //het vorige system mag dus een NextOut0 genereren (iets in machine0 steken) als dit nog niet zou bestaan 
                else
                {
                if(nextOut[0] == Long.MAX_VALUE && system[0].queued !=0)
                    getNextOut(0);    
                }
            }
        
        }
}
